package org.tau.workshop2011.semantics

import org.tau.workshop2011.parsing.AST._
import scala.collection.mutable.{Stack => MutableStack}

object SymbolResolvingVisitor {

  def analyze (node:Widget) : (Widget, GlobalSymbolTable) = {
    val srv = new SymbolResolvingVisitor
    val widget = (node accept srv).asInstanceOf[Widget]
    val gst = srv.GST

    (widget,gst)
  }

}

class SymbolResolvingVisitor extends ASTDeepCloneVisitor {

  protected val GST = new GlobalSymbolTable
  protected val symTableStack = new MutableStack[SymbolTable]
  symTableStack push GST

  def currentSymbolTable = symTableStack.head

  def pushSymbolTable(sym: SymbolTable): SymbolTable = {
    symTableStack push sym
    sym
  }

  def popSymbolTable() {
    symTableStack.pop()
  }

  def withNewSymbolTable[T <: ASTNode](nodeFunc: => T): T = {
    val env = pushSymbolTable(new LocalSymbolTable(currentSymbolTable))
    val node = nodeFunc
    node.environment = env
    popSymbolTable()
    node
  }

  def withParentSymbolTable[T <: ASTNode](nodeFunc: => T): T = {
    val node = nodeFunc
    node.environment = currentSymbolTable
    node
  }

  def withGlobalSymbolTable[T <: Widget](nodeFunc: => T): T = {
    val env = pushSymbolTable(new GlobalSymbolTable)
    val node = nodeFunc
    node.environment = env
    popSymbolTable()
    node
  }

  override def visit(node: AtomicWidget): Widget = {
    withNewSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Comparision): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Condition): Expr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Conjuction): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Container): Widget = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Disjunction): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: ExpressionAttribute): ExpressionAttribute = {
    withParentSymbolTable {
      /* The attribute shouldn't use it's own value (i.e., it's not recursive)
       * so the fact we are duplicating it with the symbol table from before it's
       * definition is actually ok. Also note that for the variable defining the
       * attribute, we don't use this class but the base duplicator in the super
       * class, to avoid wrong attempt to type resolve an undefined variable
       */
      val vari = super.visit(node.leftSide).asInstanceOf[Variable]
      val attr = new ExpressionAttribute (vari, node.realValue.cloneNode)
      /* Now we define the variable */
      currentSymbolTable.define(attr)
      attr
    }
  }

  override def visit(node: FunctionCall): FunctionCall = {
    withParentSymbolTable {
      new FunctionCall(node.function.cloneNode, node.args map {(arg) => super.visit(arg).cloneNode})
    }
  }

  override def visit(node: InitialAttribute): InitialAttribute = {
    withParentSymbolTable {
      /* The attribute shouldn't use it's own value (i.e., it's not recursive)
       * so the fact we are duplicating it with the symbol table from before it's
       * definition is actually ok. Also note that for the variable defining the
       * attribute, we don't use this class but the base duplicator in the super
       * class, to avoid wrong attempt to type resolve an undefined variable
       */
      val vari = super.visit(node.leftSide).asInstanceOf[Variable]
      val attr = new InitialAttribute (vari, node.value match { case Some(v) => Some (v.cloneNode); case None => None })
      /* Now we define the variable */
      currentSymbolTable.define(attr)
      attr
    }
  }

  override def visit(node: IterationVariable): DirectExpr = {
    /* We should not have iteration variables by this stage */
    throw new Exception("Couldn't expand the iteration macro " + node)
  }

  override def visit[T](node: Literal[T]): Literal[T] = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Negation): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Product): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Program): Program = {
    throw new Exception ("You should not call the isReadOnly(Program) method " +
      "directly for this class!")
  }

  override def visit(node: PropertyScope): Widget = {
    withNewSymbolTable {
      /* We want to first clone the attributes, then clone the widget. Since
       * when we clone the attributes, we define the variables, and variables of
       * the outer scope should be defined first (in order to be available, due
       * to name hiding) */
      val attributes = node.attributes map ((attr) => attr.cloneNode)
      new PropertyScope(node.widget.cloneNode, attributes)
    }
  }

  override def visit(node: Sum): DirectExpr = {
    withParentSymbolTable {
      super.visit(node)
    }
  }

  override def visit(node: Variable): DirectExpr = {
    withParentSymbolTable {
        currentSymbolTable.get(node, node.resultType).definition.leftSide
    }
  }
}